package cn.ztuo.bitrade.util;

import cn.ztuo.bitrade.entity.Address;
import cn.ztuo.bitrade.entity.WithdrawResult;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.bitcoinj.crypto.MnemonicCode;
import org.bitcoinj.crypto.MnemonicException;
import org.bitcoinj.wallet.DeterministicSeed;
import org.springframework.stereotype.Component;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.*;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.response.EthBlockNumber;
import org.web3j.protocol.core.methods.response.EthGasPrice;
import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.http.HttpService;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;
import sun.security.provider.SecureRandom;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

@Slf4j
@Component
public class FiworkWalletUtil {

    private static String collectionUrl = "https://mc.odf.plus/";

    private static String apikey = "CFZJ68T1EJ8IKDTZR4GGI2U3QKZ8FKNGWI";

    //private static String mainnetUrl = "https://mainnet.infura.io/v3/d03c45b5894341f0bcfcbcf631a4e2a1";

    private static String mainnetUrl ="http://47.75.90.101:8550";

    public static Web3j getWeb3j() {
        return Web3j.build(new HttpService(mainnetUrl));
    }
    public static Address createAddressForETH() throws Exception {
        ECKeyPair keyPair = Keys.createEcKeyPair();
        String privateKey = keyPair.getPrivateKey().toString(16);
        String getAddress = Keys.getAddress(keyPair);
        String ethAddress = Keys.toChecksumAddress(getAddress);
        Address address = new Address();
        address.setAddress(ethAddress);
        address.setPrivateKey(privateKey);
        return address;
    }

    /**
     * 从归集平台获取地址
     * @param userId 调用归集接口签名需要，传入真实userId即可
     * @return
     */
    public static Address getEthAddressForCollection(String userId) {
        Map<String, Object> map = new HashMap<>();
        map.put("coin", "ETH");
        map.put("lang", "lang");
        map.put("uid", userId);
        String sign = MD5Util.getMD5("ETH" + "Gs478Uq9ogaYofvh");
        map.put("sign", sign);
        System.out.println(collectionUrl + "/apicoin/api/getaddress");
        String resultString = HttpUtil.URLPost(collectionUrl + "/apicoin/api/getaddress", map);
        System.out.println(resultString);
        JSONObject jsonObject = JSONObject.parseObject(resultString);
        JSONObject data = jsonObject.getJSONObject("data");
        Address address = new Address();
        address.setAddress(data.getString("address"));
        address.setPrivateKey(data.getString("pass"));
        return address;
    }

    /**
     * 获取ETH交易记录
     * @param address    地址
     * @param startBlock 开始区块
     * @param endBolck   结束区块
     * @return
     */
    public static JSONObject getEthRecorded(String address, String startBlock, String endBolck) {
        String url = "http://api-cn.etherscan.com/api";
        HashMap<String, String> map = new HashMap<>();
        map.put("module","account");
        map.put("action","txlist");
        map.put("address",address);
        map.put("startblock",startBlock);
        map.put("endblock",endBolck);
        map.put("sort","asc");
        map.put("apikey",apikey);
        String result = HttpUtil.URLGet(url, map);
        return JSONObject.parseObject(result);
    }

    /**
     * 获取ETH代币交易记录
     * @param contractAddress 代币合约地址
     * @param address    要查询的地址
     * @param startBlock 开始区块
     * @param endBolck   结束区块
     * @return
     */
    public static JSONObject getEthTokenRecorded(String contractAddress, String address, String startBlock, String endBolck) {
        String url = "http://api-cn.etherscan.com/api?";
        HashMap<String, String> map = new HashMap<>();
        map.put("module","account");
        map.put("action","tokentx");
        map.put("contractaddress",contractAddress);
        map.put("address",address);
        map.put("startblock",startBlock);
        map.put("endblock",endBolck);
        map.put("sort","asc");
        map.put("apikey",apikey);
        String result = HttpUtil.URLGet(url, map);
        return JSONObject.parseObject(result);
    }

    /**
     * 获取手续费
     * @return
     * @throws IOException
     */
    public static BigInteger getGasPrice() throws IOException {
        Web3j web3j = getWeb3j();
        EthGasPrice gasPrice = web3j.ethGasPrice().send();
        BigInteger baseGasPrice = gasPrice.getGasPrice();
        return baseGasPrice;
    }

    /**
     * 获取最新区块
     * @return
     */
    public static BigInteger getBlockNumber() throws IOException {
        Web3j web3j = getWeb3j();
        EthBlockNumber ethBlockNumber = null;
        ethBlockNumber = web3j.ethBlockNumber().send();
        return ethBlockNumber.getBlockNumber();
    }

    /**
     * ETH提币
     * @param amount        数量
     * @param privateKey    私钥
     * @param address       地址
     * @param gasPrice      手续费
     * @param gasLimit      最大手续费
     * @return
     * @throws Exception
     */
    public static WithdrawResult withdrawForETH(BigDecimal amount, String privateKey, String address, BigInteger gasPrice, BigInteger gasLimit) throws Exception {
        Web3j web3j = getWeb3j();
        // 创建凭证
        Credentials credentials = Credentials.create(privateKey);
        EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(credentials.getAddress(), DefaultBlockParameterName.LATEST).send();
        BigInteger nonce = ethGetTransactionCount.getTransactionCount();
        // 转换单位
        BigInteger value = Convert.toWei(amount, Convert.Unit.ETHER).toBigInteger();
        log.info("value={},gasPrice={},gasLimit={},nonce={},address={}", value, gasPrice, gasLimit, nonce, address);
        // 创建交易
        RawTransaction rawTransaction = RawTransaction.createEtherTransaction(nonce, gasPrice, gasLimit, address, value);
        // 签名
        byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
        String hexValue = Numeric.toHexString(signedMessage);
        EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).send();
        String transactionHash = ethSendTransaction.getTransactionHash();
        log.info("txid = {}", transactionHash);
        WithdrawResult withdrawResult = new WithdrawResult();
        withdrawResult.setEthSendTransaction(ethSendTransaction);
        if (transactionHash != null) {
            withdrawResult.setSuccess(true);
        } else {
            withdrawResult.setSuccess(false);
        }
        return withdrawResult;
    }

    /**
     * ETH代币提币
     * @param amount        数量
     * @param privateKey    私钥
     * @param contractAddress   合约地址
     * @param address       地址
     * @param gasPrice      手续费
     * @param gasLimit      最大手续费
     * @return
     * @throws Exception
     */
    public static WithdrawResult withdrawForETHToken(BigInteger amount, String privateKey, String contractAddress, String address, BigInteger gasPrice, BigInteger gasLimit) throws Exception {
        // 提币
        Web3j web3j = getWeb3j();
        Credentials credentials = Credentials.create(privateKey);
        EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(credentials.getAddress(), DefaultBlockParameterName.LATEST).send();
        BigInteger nonce = ethGetTransactionCount.getTransactionCount();
        Function fn = new Function("transfer", Arrays.asList(new org.web3j.abi.datatypes.Address(address), new Uint256(amount)), Collections.<TypeReference<?>>emptyList());
        String data = FunctionEncoder.encode(fn);
        log.info("from={},value={},gasPrice={},gasLimit={},nonce={},address={}", credentials.getAddress(), amount, gasPrice, gasLimit, nonce, address);
        RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice, gasLimit, contractAddress, data);
        byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
        String hexValue = Numeric.toHexString(signedMessage);
        log.info("hexRawValue={}", hexValue);
        EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get();
        String transactionHash = ethSendTransaction.getTransactionHash();
        log.info("txid:" + transactionHash);
        WithdrawResult withdrawResult = new WithdrawResult();
        withdrawResult.setEthSendTransaction(ethSendTransaction);
        if (transactionHash != null) {
            withdrawResult.setSuccess(true);
        } else {
            withdrawResult.setSuccess(false);
        }
        return withdrawResult;
    }

    /**
     * 生成助记词
     * @return
     * @throws MnemonicException.MnemonicLengthException
     */
    public static List<String> createWord() throws MnemonicException.MnemonicLengthException{
        SecureRandom secureRandom = new SecureRandom();
        byte[] entropy = new byte[DeterministicSeed.DEFAULT_SEED_ENTROPY_BITS / 8];
        secureRandom.engineNextBytes(entropy);
        //生成12位助记词
        List<String>  str = MnemonicCode.INSTANCE.toMnemonic(entropy);
        return str;
    }

}
